nodejs의 event loop에 대해서 설명해 주세요

Node.js의 이벤트 루프는 비동기 작업을 처리하기 위한 중요한 구조입니다. 이를 이해하기 위해서는 Microtask, Macrotask(또는 Task Queue), 그리고 Node.js에서 제공하는 특수 API(setImmediate, setTimeout, process.nextTick)의 동작 방식을 파악해야 합니다. 이 문서에서는 setImmediatesetTimeout의 차이를 중심으로, 이를 Microtask와 Macrotask의 관점에서 비교합니다.


1. 이벤트 루프의 기본 구조

Node.js 이벤트 루프는 다음과 같은 단계로 이루어져 있습니다:

  1. Timers: setTimeoutsetInterval 콜백 처리.
  2. I/O Callbacks: I/O 작업 완료 시 실행되는 콜백 처리.
  3. Idle, Prepare: 내부적으로 사용되는 단계.
  4. Poll: 새로운 I/O 이벤트를 가져오고, 필요한 경우 콜백을 실행.
  5. Check: setImmediate 콜백 처리.
  6. Close Callbacks: 닫기 이벤트의 콜백 처리. 예: socket.on('close', ...)
   ┌───────────────────────────┐
┌─>│        timers        │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │  pending callbacks   │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
│  │    idle, prepare     │
│  └─────────────┬─────────────┘      ┌────────────────┐
│  ┌─────────────┴─────────────┐      │ incoming:   │
│  │         poll         │<─────┤ connections: │
│  └─────────────┬─────────────┘      │ data, etc.  │
│  ┌─────────────┴─────────────┐      └────────────────┘
│  │        check         │
│  └─────────────┬─────────────┘
│  ┌─────────────┴─────────────┐
└──┤    close callbacks   │
   └───────────────────────────┘

출처

Microtask와 Macrotask


2. setImmediate vs setTimeout

2.1 setTimeout

setTimeout(() => {
  console.log('setTimeout');
}, 0);

2.2 setImmediate

setImmediate(() => {
  console.log('setImmediate');
});

실행 순서 비교

코드 결과
setTimeout(() => console.log('Timeout'), 0); setImmediate(() => console.log('Immediate')); Immediate, Timeout
fs.readFile('file.txt', () => { setTimeout(...); setImmediate(...); }); Immediate, Timeout

setImmediatesetTimeout보다 항상 먼저 실행되는 것은 아니며, I/O 작업 여부에 따라 달라질 수 있습니다.


3. process.nextTick와 Promises

3.1 process.nextTick

process.nextTick(() => {
  console.log('nextTick');
});

3.2 Promises

Promise.resolve().then(() => {
  console.log('Promise');
});

실행 순서 비교

코드 결과
process.nextTick(() => console.log('nextTick')); Promise.resolve().then(() => console.log('Promise')); nextTick, Promise
setTimeout(() => console.log('Timeout'), 0); setImmediate(() => console.log('Immediate')); Immediate, Timeout

4. 전체 비교 표

API 분류 실행 시점 우선 순위
process.nextTick Microtask 현재 작업 완료 후, 이벤트 루프 이전 가장 높음
Promises Microtask 현재 작업 완료 후, Microtask Queue에서 처리 process.nextTick보다 낮음
setTimeout Macrotask Timers 단계에서 실행 낮음
setImmediate Macrotask Check 단계에서 실행 setTimeout보다 높음

5. 실습 예제

코드

setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));
Promise.resolve().then(() => console.log('Promise'));
process.nextTick(() => console.log('nextTick'));

예상 결과

  1. process.nextTick → Microtask로 즉시 실행.
  2. Promise → 다음 Microtask Queue에서 실행.
  3. setImmediate → Check 단계에서 실행.
  4. setTimeout → Timers 단계에서 실행.

출력

nextTick
Promise
setImmediate
setTimeout

6. 결론